回答這個問題前應該要先了解為什麼會有 null ,如果寫過網頁前端的讀者應該很常看到以下程式碼(以react舉例)
if(!apiData){
return <></>
}
其實大部分原因都是因為「我們不確定什麼時候有值」。最常見大概就是來自後端的資料,至於為什麼這樣會產生null ,正如前幾篇文章說過非同步要等待一段時間才會回傳結果,但為了執行緒不阻塞所以執行緒會繼續往下執行程式。
所以有些 Component 必須額外做null check ,否則在 runtime 時就會有幾秒值是null 但如果有Component 有取用這些值,就會發生runtime error,又或者是這次api request是失敗的。但在response回來前我們根本不會知道這些事情。
在 Dart SDK 2.12 版後就是會預設開啟 Sound null safety,其中最大的前提是
「如果沒有特別說明所有的type都是non-nullable的」
所以我們現在如果真的有個Type有可能是 null 那我們可以用 Type? 來表示這個變數是nullable的,像是下面例子中的 String? apiData
class Foo {
String? apiData;
Future<void> fecthData() async {
await Future.delayed(Duration(seconds: 0), () {
apiData = 'hello wrold';
});
}
}
// ...
final foo = Foo()
foo.apiData.length // 這行會出錯
所以當我們使用 foo.apiData.length 時,就會靜態檢查期間就有error出現:

而如果我們硬要run 就會直接跳出error

從這個例子我們就可以看到null safety 對我們開發有多大的幫助,因為我們不用實際run就可以知道哪部分的code可能會有null相關的runtime error。
那我們要怎麼操作nullable type的變數呢?
基本上有兩種方式 ? !
?. 就跟JS的optional chaining一樣:如果存取到null就直接return null而不是直接throw error
print(foo.apiData?.length);
// null
! 則是表示這個變數「現在」一定不是null
我們先宣告一個input type 為 String 的function
String concatString(String input) => input + '---';
// ...
final foo = Foo();
await foo.fecthData();
concatString(foo.apiData);
concatString(foo.apiData!);
print(foo.apiData!.length);

會發現即使我們是在 fetchData() 後再取用 foo.apiData 這裡的type 依然是 String? ,雖然我們能夠確定他一定有值,但這是靜態檢查不出來的。所以我們可以加上! 讓編譯器知道:
「這個值雖然是nullable type,但它現在一定不是null哦」
所以我們就能將 原本是 String? 的變數放進去只接受 String 的function裡,也能夠正確的調用 .length 了。
還記得在很久很久之前看到的 late 嗎
先在一個 class 中先用 final 宣告兩個成員然後其中一個加上 late :
late final String a;
final String b;
Test(this.b);
void setInitValue() {
a = 'a';
}

如果在constructor沒有放this.b會看到hint 只有跳出 b 需要 initialize
但late 只是可以讓 null檢查延遲到運行而不是編譯,所以如果忘記 initialize 在runtime還是會有error跳出來。
final test = Test('');
//test.setInitValue();
print(test.a);

今天的程式碼:
https://github.com/zxc469469/dart-playground/tree/Day12/null-safety
Sound null safety 只是很大一部份提升我們在開發時體驗,但這不代表一定不會有bug,畢竟你可能真的沒有去set資料導致你之後! 其實是標爽的,又或者標 late 的變數忘記 initialize,另外還有一個好處就是會提升程式的編譯效率,因為編譯器可以少做一些null check。
而明天將是Dart篇的最後一篇文章,也就是稍微提一下 Functional programming(FP)的概念。之後就要進入Flutter的世界了~
參考資料:
https://juejin.cn/post/6958965184631144478